package proj3;

import java.util.Arrays;

import misc.not_Legal_Exception;
import evolution_superclasses.Evolutionary_Loop;
import evolution_superclasses.Genotype;
import evolution_superclasses.Phenotype;

public class Proj3_Loop extends Evolutionary_Loop {
	private Proj3_Population _p3pop;
	private Proj3_Genetic_Operators _p3go;
	
	private String _plotPath = "";
	
	static boolean catchAll;

	public Proj3_Loop() {
		super();
		set_plotList(new boolean[]{true,true,false,true});
	}
	
	public static void main(String[] args) {
		Proj3_Loop p3 = new Proj3_Loop();
//		boolean die = false;
		try {
			p3.begin();
//			p3.test();
//			p3.simulatePhentype();
//			p3.testCatchAll();
		} catch (Exception e) {
//			die = true;
			e.printStackTrace();
		}
//		if(!die){
//			try {
//				System.out.println("Again? (1/0)");
//				if(p3._ih.readInt()==1){
//					main(null);
//					return;
//				}
//			} catch (Exception e) {
//				e.printStackTrace();
//			}
//		}
	}
	
	//test genetic operators
	public void test() throws not_Legal_Exception{
		initializeEndConditions(200);
		initializePopulationSize(2);
		initializeGenotypeSize(34*8);
		initializeGeneticOperatiors(0.0, 1);
		initializeValueMutationRate(0.0);
		initializeEliteism(0);
		initializeAdultPoolSize(0);//0=_popsize
		initializeSelectionProtocol(0);
		initializePopulation();
		initializeSelectionMechanism(2);
		_bsm.set_sigma_estimated(40); //sigma scaling

		System.out.println("before");
		for(Genotype g : _p3pop.get_genotypes()){
			System.out.println(g+"\n");
		}
		
		initializeAndDevelopGeneration();
		_bsp.set_adultPool(_generation);
		_bsm.set_parents(_generation);
		incrementGenerationNumber();
		createNewPopulation();
		System.out.println("\nafter");
		for(Genotype g : _p3pop.get_genotypes()){
			System.out.println(g+"\n");
		}
	}
	
	public void simulatePhentype(){
		Proj3_Genotype g = new Proj3_Genotype(34*8);
		Proj3_Phenotype p = (Proj3_Phenotype) g.generatePhenotype();
		p.set_printInputs(true);
		Neuron[] neurons;
		Double[][] weights;
		Input[] inputs = new Input[5];
		for (int i = 0; i < 5; i++) inputs[i] = new Input();
		Neuron motor_left;
		Neuron motor_right;

		double[] gains = {2.3490, 4.2784, 4.2000, 3.7922};
		double[] tcs = {1.9490, 1.8627, 1.2275, 1.9373};
		double[] w = {
				-3.82, -0.25,
				-0.22,	-3.43,
				2.88,	-4.14,
				0.22,	-1.39,
				-3.71,	-1.12,
				3.86,	2.69,	-3.00,	-4.80,
				0.49,	3.27,	1.27,	2.45,
								3.27,	-3.27,
								0.96,	4.92,
				-1.02,	-1.37,	-0.31,	-4.47};
		
		neurons = new Neuron[] {
			new SensorNeuron(new Input[]{inputs[0]}),	// sensory input node 1
			new SensorNeuron(new Input[]{inputs[1]}),	// sensory input node 2
			new SensorNeuron(new Input[]{inputs[2]}),	// sensory input node 3
			new SensorNeuron(new Input[]{inputs[3]}),	// sensory input node 4
			new SensorNeuron(new Input[]{inputs[4]}),	// sensory input node 5
			new Neuron(0.0, gains[0], tcs[0]),				// hidden layer node 1
			new Neuron(0.0, gains[1], tcs[1]),				// hidden layer node 2
			new Neuron(0.0, gains[2], tcs[2]),				// motor control layer node 1
			new Neuron(0.0, gains[3], tcs[3]),				// motor control layer node 2
			new BiasNueron()										// bias node
		};
		motor_left = neurons[7];
		motor_right = neurons[8];
	
		weights = new Double[][] {
			//[from]/[to]
				{ null, null, null, null, null, w[0], w[1], null, null, null },
				{ null, null, null, null, null, w[2], w[3], null, null, null },
				{ null, null, null, null, null, w[4], w[5], null, null, null },
				{ null, null, null, null, null, w[6], w[7], null, null, null },
				{ null, null, null, null, null, w[8], w[9], null, null, null },
				{ null, null, null, null, null,w[10],w[11],w[12],w[13], null },
				{ null, null, null, null, null,w[14],w[15],w[16],w[17], null },
				{ null, null, null, null, null, null, null,w[18],w[19], null },
				{ null, null, null, null, null, null, null,w[20],w[21], null },
				{ null, null, null, null, null, w[22],w[23],w[24],w[25], null },
		};

		p.setMotorLeft(motor_left);
		p.setMotorRight(motor_right);
		p.setNeurons(neurons);
		p.setWeights(weights);
		p.setInputs(inputs);

		p.updateNeurons(new boolean[]{true,true, true,true, true});
		System.out.println("\n----------------------------\n");
		p.updateNeurons(new boolean[]{false,false, false,false, true});
		System.out.println("\n----------------------------\n");
		p.updateNeurons(new boolean[]{false,false, false,false, false});
		System.out.println("\n----------------------------\n");
		p.updateNeurons(new boolean[]{false,false, false,false, false});
		System.out.println("");
		
		System.out.println(p.printOs());
		System.out.println(p.printCTRNN());
		System.out.println(p.printAction());
	}
	
	public void testCatchAll() throws not_Legal_Exception{
		set_title("svamor");
		_plotPath = "/Proj3/";
		set_filename(_plotPath+"chart");
		_plot = false;
		int N = 100;
		double total = 0;
		for(int i = 0; i < N; i++){
			clearArrayLists();
			
			initializeCatchAll(true);
			initializePrint(false);
			initializeEndConditions(200);
			initializePopulationSize(100);
			initializeGenotypeSize(34*8);
			initializeGeneticOperatiors(1, 0.1);
			initializeValueMutationRate(0.01);
			initializeEliteism(1);
			initializeAdultPoolSize(0);//0=_popsize
			initializeSelectionProtocol(0);
			initializePopulation();
			initializeSelectionMechanism(3);
			_bsm.set_sigma_estimated(40); //sigma scaling
			
			evolutionLoop();
			total += get_bestFitness();
		}
		System.out.println(total/N);
	}
	
	@Override  //Our values are in comments
	public void begin() throws Exception{
		clearArrayLists();

		set_title("svamor");
		_plotPath = "/Proj3/";
		set_filename(_plotPath+"chart");
		_plot = true;
		
		initializeCatchAll(null); //false
		initializeMoveRight(null); //false
		initializePrint(true); //false
		initializeModifyNetwork(null); //false
		initializeModifyTcRange(null); //false
		initializeEndConditions(-1); //200
		initializePopulationSize(-1); //false
		initializeGenotypeSize(34*8);
		initializeGeneticOperatiors(-1, -1); //1, 0.1
		initializeValueMutationRate(-1); //0.01
		initializeEliteism(-1); //-1
		initializeAdultPoolSize(-1);//0=_popsize
		initializeSelectionProtocol(-1); // 0
		initializePopulation();
		initializeSelectionMechanism(-1); // 30
		_bsm.set_sigma_estimated(40); //sigma scaling
		
		evolutionLoop();
		if(!_print)//Print them anyway
			printEndResults();
	}
	
	protected void initializeCatchAll(Boolean c) {
		if (c != null) {
			catchAll = c;
			return;
		}
		try {
			System.out.println("Do you want to 1) catch all; or 2) avoid bigger than yourself?");
			catchAll = _ih.readInt() == 1;
		} catch (Exception e) {
			System.out.println("Illegal value. Try again.");
			initializeCatchAll(null);
		}
	}
	
	protected void initializeModifyNetwork(Boolean m) {
		if (m != null) {
			Proj3_Phenotype.modify_network = m;
			return;
		}
		try {
			System.out.println("Do you want the network to be 1) default; or 2) modified to eliminate self-recursion in the hidden layer?");
			Proj3_Phenotype.modify_network = _ih.readInt() == 2;
		} catch (Exception e) {
			System.out.println("Illegal value. Try again.");
			initializeModifyNetwork(null);
		}
	}
	
	protected void initializeModifyTcRange(Boolean m) {
		if (m != null) {
			Proj3_Phenotype.modify_tc_range = m;
			return;
		}
		try {
			System.out.println("Do you want the time constant range to be 1) default, [+1.0,+2.0]; or 2) modified to be in [+2.0,+3.0]?");
			Proj3_Phenotype.modify_tc_range = _ih.readInt() == 2;
		} catch (Exception e) {
			System.out.println("Illegal value. Try again.");
			initializeModifyTcRange(null);
		}
	}
	
	protected void initializeMoveRight(Boolean m) {
		if (m != null) {
			Proj3_Phenotype.move_right = m;
			return;
		}
		try {
			System.out.println("Do you want the boxes to move 1) vertically; or 2) diagonally?");
			Proj3_Phenotype.move_right = _ih.readInt() == 2;
		} catch (Exception e) {
			System.out.println("Illegal value. Try again.");
			initializeMoveRight(null);
		}
	}

	protected void initializeValueMutationRate(double m) {
		if(m>-1){
			_p3go.set_valueMutationRate(m);
			return;
		}
		try {
			System.out.println("At what rate should genotype param mutate? (type double, between 0 and 1)");
			double tmp = _ih.readDouble();
			if(tmp < 0 || tmp > 1)
				throw new IllegalArgumentException();
			_p3go.set_valueMutationRate(tmp);
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeValueMutationRate(m);
			return;
		}
	}

	@Override
	protected void createNewPopulation() {		
		int nr = 0, i = 0;
		Genotype[] tmp = new Genotype[_popSize];	
		
		if(_generationLimit==0){
			((Proj3_Phenotype)_bestInduvidual.get_phenotype()).visualize();
		}

		while(i < _freepass){
			Proj3_Genotype bg1 = new Proj3_Genotype(_genSize);

			bg1.set_binaryString(new String(((Proj3_Genotype)_bsp.get_adultPool()[i].get_genotype()).get_binaryString()));
			tmp[i++] = bg1;
		}
		
		while(nr < _popSize-_freepass){
			Proj3_Genotype bg1 = new Proj3_Genotype(_genSize);
			Proj3_Genotype bg2 = new Proj3_Genotype(_genSize);

			bg1.set_binaryString(new String(((Proj3_Genotype)_bsm.get_parents()[nr++].get_genotype()).get_binaryString()));
			bg2.set_binaryString(new String(((Proj3_Genotype)_bsm.get_parents()[((nr++)%_bsm.get_parents().length)].get_genotype()).get_binaryString()));

			if( _crossOverRate > _rand.nextDouble())
				_p3go.crossOver(bg1, bg2);

			if( _mutationRate > _rand.nextDouble())
				_p3go.mutation(bg1);
			if( _mutationRate > _rand.nextDouble())
				_p3go.mutation(bg2);
			
			tmp[i++] = bg1;
			if(i<_popSize) //In case it's an odd number.
				tmp[i++] = bg2;	
		}
		_p3pop.set_genotypes(tmp);
	}

	@Override
	protected void initializeAndDevelopGeneration() throws not_Legal_Exception {
		int index = 0;
    	double variance = 0,standardDev, avg,total = 0;
		_generation = new Proj3_Phenotype[_popSize];
		
		for(Genotype g : _p3pop.get_genotypes()){
			Phenotype p = g.generatePhenotype();
			_generation[index++] = p;
			total += p.get_fitness();
		}
		
		avg = total/_popSize;

		for(Phenotype p : _generation){
    		variance += Math.pow(p.get_fitness()-avg,2);
    	}
    	standardDev = Math.sqrt(variance/_popSize);

    	Arrays.sort(_generation);
		set_bestFitness(_generation[0].get_fitness());
		set_bestInduvidual(_generation[0].get_genotype());
		set_totalFitness(total);
		set_worstFitness(_generation[_popSize-1].get_fitness());

		_max.add(get_bestFitness());
		_avg.add(avg);
		_min.add(get_worstFitness());
		_stdDerivance.add(standardDev);
	}
	
	@Override
	protected void printEndResults() {
		System.out.println("End fitness: "+_endBestFitness+" after "+(get_generationNumber()-1)+" generations");
		if(_print){
			System.out.println("The best individual was this this:");
			System.out.println(_bestInduvidual.get_phenotype());
			System.out.println("\nTraits:");
			System.out.println(((Proj3_Phenotype)_bestInduvidual.get_phenotype()).printCTRNN());
		}
	}

	@Override
	protected void initializeFitnessEvaluation(int i)
			throws not_Legal_Exception, Exception {
		throw new not_Legal_Exception("Woot woot woot??? There be no explisit class for this!");
	}

	@Override
	protected void initializeGeneticOperatiors(double m, double c) {
		if(m>-1){
			_mutationRate = m;
			_crossOverRate = c;
			_p3go = new Proj3_Genetic_Operators(_genSize);
			return;
		}
		try {
			System.out.println("At what rate should genotypes mutate? (type double, between 0 and 1)");
			_mutationRate = _ih.readDouble();
			System.out.println("At what rate should genotypes suffer crossOvers? (type double, between 0 and 1)");
			_crossOverRate = _ih.readDouble();
			if(_mutationRate < 0 || _mutationRate > 1 || _crossOverRate < 0 || _crossOverRate > 1)
				throw new IllegalArgumentException();
		} catch (Exception e) {
			System.out.println("Illegal value detected, try again");
			initializeGeneticOperatiors(m,c);
			return;
		}
		_p3go = new Proj3_Genetic_Operators(_genSize);
	}

	@Override
	protected void initializePopulation() {
		_p3pop = new Proj3_Population(_genSize, _popSize);
	}

}
